#include "mainwindow.h"
#include "ui_mainwindow.h"

#include <QMessageBox>
#include "math.h"
#include "stdio.h"
#include "windows.h"

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);


   // ui->pushButton_2->setHidden(true);
    //ui->label_3->setHidden(true);
    //ui->spinBox->setHidden(true);
    difference = new QCustomPlot();
    difference->setMinimumSize(300,300);
    difference->setWindowTitle("x_v");
    difference->show();

    phasePortrait = new QCustomPlot();
    phasePortrait->setWindowTitle("v_vDerivative");
    phasePortrait->setMinimumSize(300,300);

    x_vDer = new QCustomPlot();
    x_vDer->setWindowTitle("vDerivative_x");
    x_vDer->setMinimumSize(300,300);

    table = new QTableWidget(0);
    splineTable = new QTableWidget(0);


}

MainWindow::~MainWindow()
{
    delete table;
    delete splineTable;
    delete ui;
}

bool MainWindow::event(QEvent *mEvent)
{
    if (mEvent->type()==QEvent::Close)
    {
        x_vDer->close();
        phasePortrait->close();
        difference->close();
        table->close();
        splineTable->close();
        this->close();
        return true;
    }
    return QMainWindow::event(mEvent);
}

double u(double x, double u0)
{
    return u0*exp(-1.5*x);//pow(1+x*x,-1.5);
}

double ftest(double x, double u)
{
    return -1.5*u;
}

double fmain1(double x, double u)
{
    return pow(1+x*x,-1.5)*u*u+u-u*u*u*sin(10*x);
}

double fmain2(double x, double u, double uDer)
{
    return uDer;
}
double gmain2(double x, double u, double uDer)
{
    double a=1.0;
    double b=1.0;
    return a*uDer*uDer+b*u;
}

void MainWindow::plotFunc2(QVector<double>& x, QVector<double>& y, QCustomPlot* holst, QPen color, QString xlabel, QString ylabel)
{
    //difference->clearGraphs();
    holst->addGraph();

    holst->graph(holst->graphCount()-1)->setData(x, y);
    // give the axes some labels:
    holst->xAxis->setLabel(xlabel);
    holst->yAxis->setLabel(ylabel);
    holst->graph(holst->graphCount()-1)->setPen(color);
    //ui->graphWidget->xAxis->setRange(x[0]-0.5,x[x.size()+1]+0.5);
    //ui->graphWidget->yAxis->setRange(-2.5,2.5);
    //ui->graphWidget->graph(ui->graphWidget->graphCount()-1)->rescaleAxes(true);
    //difference->graph(0)->rescaleAxes();
    holst->replot();
}

double MainWindow::computeNext(double x, double v, double h, double (*f)(double,double))
{
    return v+h*f(x+h/2.0, v+h/2.0*f(x,v));
}
void MainWindow::computeNextForSystem(const double x, const double v, const double vDer, const double h,
                                      double (*f1)(double,double, double),
                                      double (*f2)(double,double, double),
                                      double & vNext, double& vDerNext)
{
    vNext =     v+h*f1(x+h/2, v+h/2*f1(x,v,vDer), vDer+h/2*f2(x,v,vDer));
    vDerNext =  vDer+h*f2(x+h/2, v+h/2*f1(x,v,vDer), vDer+h/2*f2(x,v,vDer));
}


vec3 fLab(double x, vec3 v)
{
    double k=2;
    double J=10;
    double F=1;
    double n=2;
    double g=9.8;
    double b=1;
    double m=1;
    return vec3(k/J*cos(v.t2)-F/J, v.t3, n*n*v.t1*v.t1*sin(v.t2)*cos(v.t2)-g*sin(v.t2)-b/m*v.t3);
}


void MainWindow::computeNextForSystemRK4(const double x, const vec3 v, const double h,
                                      vec3 (*f)(double,vec3),
                                      vec3 & vNext)
{
    vec3 k1 = f(x,v);
    vec3 k2 = f(x+h/2.0,v+h/2.0*k1);
    vec3 k3 = f(x+h/2.0,v+h/2.0*k2);
    vec3 k4 = f(x+h,v+h*k3);
    vNext = v + h/6.0*(k1+2*k2+2*k3+k4);
}


void MainWindow::on_pushButton_3_clicked()
{
    phasePortrait->show();
    x_vDer->hide();
    //Main task 1
    const int n = ui->spinBox_2->value();
    const int maxCounter = ui->spinBox_4->value();
    double h = 1.0/n;
    double a=0;
    const bool controlError = ui->radioButton_3->isChecked() ? false : true; // No or yes for controlling error

    bool t;
    const double eps = ui->lineEdit->text().toDouble(&t);
    const double u0 = ui->lineEdit_2->text().toDouble(&t);


    QVector<double> x(maxCounter+1), v(maxCounter+1), localErrorEst(maxCounter+1), udiff(maxCounter+1);

    int* c1 = new int[maxCounter+1];
    int* c2 = new int[maxCounter+1];
    int sumC1=0, sumC2=0;

    double* v2 = new double[maxCounter+1];
    x[0]=a;
    localErrorEst[0]=0;
    v2[0]=v[0]=u0;
    udiff[0] = fmain1(a, u0);
    memset(c1,0,sizeof(int)*(maxCounter+1));
    memset(c2,0,sizeof(int)*(maxCounter+1));

    int counter=1;
    double currentEps=1.0;
    double maxLocalError = -1;
    double maxH=-1, minH=1e6, maxHX, minHX;

    do
    {
        x[counter] = x[counter-1]+h;
        v[counter] = computeNext(x[counter-1], v[counter-1], h,fmain1);
        v2[counter] = computeNext(x[counter-1]+h/2, computeNext(x[counter-1],v[counter-1] ,h/2,fmain1 ) ,h/2,fmain1 );
        localErrorEst[counter] = (v2[counter]-v[counter])/3.0; //p==2
        udiff[counter] = fmain1(x[counter], v[counter]);
        if (controlError)
        {
            if (fabs(localErrorEst[counter])<eps/8.0)
            {
                h*=2.0;
                c2[counter]++;
                sumC2++;
            }
            else if (fabs(localErrorEst[counter])>eps)
            {
                h/=2.0;
                c1[counter]++;
                sumC1++;
                continue;
            }
        }

        if (fabs(localErrorEst[counter])>maxLocalError)
            maxLocalError=fabs(localErrorEst[counter]);
        if (h>maxH)
        {
            maxH=h;
            maxHX=x[counter];
        }
        if (h<minH)
        {
            minH=h;
            minHX=x[counter];
        }
        counter++;
        //currentEps = normRazn(x0,x1,n,m);
    } while (currentEps>eps && counter <= maxCounter);

    QMessageBox msgBox;
    //msgBox.setText(QString("Was done %1 iterations.\n Accuracy %2\nMax diff |u-v| %3 in (i,j)=(%4,%5)").arg(counter).arg(QString::number(currentEps, 'e', 5)).arg(maxDiff).arg(maxI).arg(maxJ));
    //msgBox.exec();

    ui->label_6->setText(QString("Was done %1 iterations").arg(counter));
    ui->label_7->setText(QString("Total divisions %1; total doubling %2").arg(QString::number(sumC1)).arg(sumC2));
    ui->label_8->setText(QString("Max local error est = %1").arg(QString::number(maxLocalError, 'e', 5)));
    ui->label_9->setText(QString("Max h = %1 in x= %2").arg(maxH).arg(maxHX));
    ui->label_11->setText(QString("Min h = %1 in x= %2").arg(minH).arg(minHX));
    ui->label_2->setText("");
    //ui->label_2->setText(QString("Max |u-v| = %1 in x= %2").arg(maxDiff).arg(maxDiffX));

    difference->clearGraphs();
    plotFunc2(x,v, difference);

    difference->graph(0)->rescaleAxes();
    //difference->graph(1)->rescaleAxes();
    difference->replot();

    phasePortrait->clearGraphs();
    plotFunc2(v,udiff, phasePortrait, QPen(Qt::red), "u", "u'");

    phasePortrait->graph(0)->setLineStyle(QCPGraph::lsNone);
    phasePortrait->graph(0)->setScatterStyle(QCPScatterStyle(QCPScatterStyle::ssCircle, 3));
    phasePortrait->graph(0)->rescaleAxes();
    phasePortrait->replot();

    if (ui->checkBox->checkState() == Qt::Checked)
    {
        table->setColumnCount(8);
        table->setRowCount(counter+1);
        table->setWindowTitle("Main task 1");
        QStringList headers;
        for (int i=0; i<counter; i++)
            headers.append(QString::number(i));
        table->setVerticalHeaderLabels(headers);
        QStringList vheaders;
        vheaders.append("x_i");
        vheaders.append("v_i");
        vheaders.append("v2_i");
        vheaders.append("v_i-v2_i");
        vheaders.append("OLP");
        vheaders.append("h_i");
        vheaders.append("C1");
        vheaders.append("C2");
        table->setHorizontalHeaderLabels(vheaders);
        //table->verticalHeader()->hide();
        //table->setColumnWidth(0,35);


        for (int i=0; i<counter; i++)
        {
            double H = 1.0/n;
            if (i>0)
                H = x[i]-x[i-1];
            table->setItem(i,0,new QTableWidgetItem(QString::number(x[i],'g',4)));
            table->setItem(i,1,new QTableWidgetItem(QString::number(v[i],'g',4)));
            table->setItem(i,2,new QTableWidgetItem(QString::number(v2[i],'g',4)));
            table->setItem(i,3,new QTableWidgetItem(QString::number(v[i]-v2[i],'g',4)));
            table->setItem(i,4,new QTableWidgetItem(QString::number(localErrorEst[i],'g',4)));
            table->setItem(i,5,new QTableWidgetItem(QString::number(H,'g',4)));
            table->setItem(i,6,new QTableWidgetItem(QString::number(c1[i])));
            table->setItem(i,7,new QTableWidgetItem(QString::number(c2[i])));
        }
        table->show();

    }

    delete[] c1;
    delete[] c2;
}


void MainWindow::on_pushButton_4_clicked()
{
    x_vDer->hide();
    phasePortrait->hide();
    // Test task
    const int n = ui->spinBox_2->value();
    const int maxCounter = ui->spinBox_4->value();
    double h = 1.0/n;
    double a=0;
    const bool controlError = ui->radioButton_3->isChecked() ? false : true; // No or yes for controlling error

    bool t;
    const double eps = ui->lineEdit->text().toDouble(&t);
    const double u0 = ui->lineEdit_2->text().toDouble(&t);


    QVector<double> x(maxCounter+1),U(maxCounter+1), v(maxCounter+1), localErrorEst(maxCounter+1);

    int* c1 = new int[maxCounter+1];
    int* c2 = new int[maxCounter+1];
    int sumC1=0, sumC2=0;

    double* v2 = new double[maxCounter+1];
    x[0]=a;
    localErrorEst[0]=0;
    v2[0]=U[0]=v[0]=u(x[0],u0);
    memset(c1,0,sizeof(int)*(maxCounter+1));
    memset(c2,0,sizeof(int)*(maxCounter+1));

    int counter=1;
    double currentEps=1.0;
    double maxLocalError = -1;
    double maxH=-1, minH=1e6, maxHX, minHX;
    double maxDiff=-1, maxDiffX;
    do
    {
        x[counter] = x[counter-1]+h;
        v[counter] = computeNext(x[counter-1], v[counter-1], h,ftest);
        U[counter] = u(x[counter],u0);
        v2[counter] = computeNext(x[counter-1]+h/2, computeNext(x[counter-1],v[counter-1] ,h/2,ftest ) ,h/2,ftest );
        localErrorEst[counter] = (v2[counter]-v[counter])/3.0; //p==2

        if (controlError)
        {
            if (fabs(localErrorEst[counter])<eps/8.0)
            {
                h*=2.0;
                c2[counter]++;
                sumC2++;
            }
            else if (fabs(localErrorEst[counter])>eps)
            {
                h/=2.0;
                c1[counter]++;
                sumC1++;
                continue;
            }
        }

        if (fabs(localErrorEst[counter])>maxLocalError)
            maxLocalError=fabs(localErrorEst[counter]);
        if (fabs(U[counter]-v[counter])>maxDiff)
        {
            maxDiff = fabs(U[counter]-v[counter]);
            maxDiffX = x[counter];
        }
        if (h>maxH)
        {
            maxH=h;
            maxHX=x[counter];
        }
        if (h<minH)
        {
            minH=h;
            minHX=x[counter];
        }
        counter++;
        //currentEps = normRazn(x0,x1,n,m);
    } while (currentEps>eps && counter <= maxCounter);

    QMessageBox msgBox;
    //msgBox.setText(QString("Was done %1 iterations.\n Accuracy %2\nMax diff |u-v| %3 in (i,j)=(%4,%5)").arg(counter).arg(QString::number(currentEps, 'e', 5)).arg(maxDiff).arg(maxI).arg(maxJ));
    //msgBox.exec();

    ui->label_6->setText(QString("Was done %1 iterations").arg(counter));
    ui->label_7->setText(QString("Total divisions %1; total doubling %2").arg(QString::number(sumC1)).arg(sumC2));
    ui->label_8->setText(QString("Max local error est = %1").arg(QString::number(maxLocalError, 'e', 5)));
    ui->label_9->setText(QString("Max h = %1 in x= %2").arg(maxH).arg(maxHX));
    ui->label_11->setText(QString("Min h = %1 in x= %2").arg(minH).arg(minHX));
    ui->label_2->setText(QString("Max |u-v| = %1 in x= %2").arg(maxDiff).arg(maxDiffX));

    difference->clearGraphs();
    plotFunc2(x,U, difference);
    plotFunc2(x,v, difference, QPen(Qt::red));
    difference->graph(0)->rescaleAxes();
    difference->graph(1)->rescaleAxes();
    difference->replot();

    if (ui->checkBox->checkState() == Qt::Checked)
    {
        table->setColumnCount(10);
        table->setRowCount(counter+1);
        table->setWindowTitle("Test task");
        QStringList headers;
        for (int i=0; i<counter; i++)
            headers.append(QString::number(i));
        table->setVerticalHeaderLabels(headers);
        QStringList vheaders;
        vheaders.append("x_i");
        vheaders.append("v_i");
        vheaders.append("v2_i");
        vheaders.append("v_i-v2_i");
        vheaders.append("OLP");
        vheaders.append("h_i");
        vheaders.append("C1");
        vheaders.append("C2");
        vheaders.append("u_i");
        vheaders.append("|u_i-v_i|");
        table->setHorizontalHeaderLabels(vheaders);
        //table->verticalHeader()->hide();
        //table->setColumnWidth(0,35);


        for (int i=0; i<counter; i++)
        {
            double H = 1.0/n;
            if (i>0)
                H = x[i]-x[i-1];
            table->setItem(i,0,new QTableWidgetItem(QString::number(x[i],'g',4)));
            table->setItem(i,1,new QTableWidgetItem(QString::number(v[i],'g',4)));
            table->setItem(i,2,new QTableWidgetItem(QString::number(v2[i],'g',4)));
            table->setItem(i,3,new QTableWidgetItem(QString::number(v[i]-v2[i],'g',4)));
            table->setItem(i,4,new QTableWidgetItem(QString::number(localErrorEst[i],'g',4)));
            table->setItem(i,5,new QTableWidgetItem(QString::number(H,'g',4)));
            table->setItem(i,6,new QTableWidgetItem(QString::number(c1[i])));
            table->setItem(i,7,new QTableWidgetItem(QString::number(c2[i])));
            table->setItem(i,8,new QTableWidgetItem(QString::number(U[i],'g',4)));
            table->setItem(i,9,new QTableWidgetItem(QString::number(fabs(U[i]-v[i]),'g',4)));
        }
        table->show();

    }    

    delete[] c1;
    delete[] c2;
}

void MainWindow::on_pushButton_5_clicked()
{
    //Main task 2
    const int n = ui->spinBox_2->value();
    const int maxCounter = ui->spinBox_4->value();
    double h = 1.0/n;
    double a=0;
    const bool controlError = ui->radioButton_3->isChecked() ? false : true; // No or yes for controlling error

    bool t;
    const double eps = ui->lineEdit->text().toDouble(&t);
    const double u0 = ui->lineEdit_2->text().toDouble(&t);
    const double uDer0 = ui->lineEdit_3->text().toDouble(&t);


    QVector<double> x(maxCounter+1), localErrorEst(maxCounter+1);
    QVector<double> vDer(maxCounter+1), v(maxCounter+1);
    QVector<double> v2Der(maxCounter+1), v2(maxCounter+1);

    int* c1 = new int[maxCounter+1];
    int* c2 = new int[maxCounter+1];
    int sumC1=0, sumC2=0;

    x[0]=a;
    localErrorEst[0]=0;
    v2[0]=v[0]=u0;
    v2Der[0]=vDer[0]=uDer0;
    memset(c1,0,sizeof(int)*(maxCounter+1));
    memset(c2,0,sizeof(int)*(maxCounter+1));

    int counter=1;
    double currentEps=1.0;
    double maxLocalError = -1;
    double maxH=-1, minH=1e6, maxHX, minHX;

    do
    {
        x[counter] = x[counter-1]+h;

        computeNextForSystem(x[counter-1], v[counter-1], vDer[counter-1], h, fmain2, gmain2, v[counter], vDer[counter]);

        double tempv2 = v2[counter];
        double tempv2Der = v2Der[counter];
        computeNextForSystem(x[counter-1], v[counter-1], vDer[counter-1], h/2, fmain2, gmain2, tempv2, tempv2Der);
        computeNextForSystem(x[counter-1]+h/2, tempv2, tempv2Der, h/2, fmain2, gmain2, v2[counter], v2Der[counter]);

        double s1 = (v2[counter]-v[counter])/3.0;
        double s2 = (v2Der[counter]-vDer[counter])/3.0;
        localErrorEst[counter] = sqrt(s1*s1+s2*s2); //p==2
        if (controlError)
        {
            if (fabs(localErrorEst[counter])<eps/8.0)
            {
                h*=2.0;
                c2[counter]++;
                sumC2++;
            }
            else if (fabs(localErrorEst[counter])>eps)
            {
                h/=2.0;
                c1[counter]++;
                sumC1++;
                continue;
            }
        }

        if (fabs(localErrorEst[counter])>maxLocalError)
            maxLocalError=fabs(localErrorEst[counter]);
        if (h>maxH)
        {
            maxH=h;
            maxHX=x[counter];
        }
        if (h<minH)
        {
            minH=h;
            minHX=x[counter];
        }
        counter++;
    } while (currentEps>eps && counter <= maxCounter);

    QMessageBox msgBox;
    //msgBox.setText(QString("Was done %1 iterations.\n Accuracy %2\nMax diff |u-v| %3 in (i,j)=(%4,%5)").arg(counter).arg(QString::number(currentEps, 'e', 5)).arg(maxDiff).arg(maxI).arg(maxJ));
    //msgBox.exec();

    ui->label_6->setText(QString("Was done %1 iterations").arg(counter));
    ui->label_7->setText(QString("Total divisions %1; total doubling %2").arg(QString::number(sumC1)).arg(sumC2));
    ui->label_8->setText(QString("Max local error est = %1").arg(QString::number(maxLocalError, 'e', 5)));
    ui->label_9->setText(QString("Max h = %1 in x= %2").arg(maxH).arg(maxHX));
    ui->label_11->setText(QString("Min h = %1 in x= %2").arg(minH).arg(minHX));
    ui->label_2->setText("");
    //ui->label_2->setText(QString("Max |u-v| = %1 in x= %2").arg(maxDiff).arg(maxDiffX));

    difference->clearGraphs();
    plotFunc2(x,v, difference);
    difference->graph(0)->rescaleAxes();
    difference->replot();

    phasePortrait->clearGraphs();
    plotFunc2(v,vDer, phasePortrait, QPen(Qt::red), "u", "u'");
    phasePortrait->graph(0)->rescaleAxes();
    phasePortrait->graph(0)->setLineStyle(QCPGraph::lsNone);
    phasePortrait->graph(0)->setScatterStyle(QCPScatterStyle(QCPScatterStyle::ssCircle, 3));
    phasePortrait->replot();

    x_vDer->clearGraphs();
    plotFunc2(x,vDer, x_vDer, QPen(Qt::green), "x", "u'");
    x_vDer->graph(0)->rescaleAxes();
    x_vDer->replot();

    x_vDer->show();
    phasePortrait->show();

    if (ui->checkBox->checkState() == Qt::Checked)
    {
        table->setColumnCount(8);
        table->setRowCount(counter);
        table->setWindowTitle("Main task 2");
        QStringList headers;
        for (int i=0; i<counter+1; i++)
            headers.append(QString::number(i));
        table->setVerticalHeaderLabels(headers);
        QStringList vheaders;
        vheaders.append("x_i");
        vheaders.append("v_i");
        vheaders.append("v2_i");
        vheaders.append("v_i-v2_i");
        vheaders.append("OLP");
        vheaders.append("h_i");
        vheaders.append("C1");
        vheaders.append("C2");
        table->setHorizontalHeaderLabels(vheaders);
        //table->verticalHeader()->hide();
        //table->setColumnWidth(0,35);


        for (int i=0; i<counter; i++)
        {
            double H = 1.0/n;
            if (i>0)
                H = x[i]-x[i-1];
            table->setItem(i,0,new QTableWidgetItem(QString::number(x[i],'g',4)));
            table->setItem(i,1,new QTableWidgetItem(QString::number(v[i],'g',4)));
            table->setItem(i,2,new QTableWidgetItem(QString::number(v2[i],'g',4)));
            table->setItem(i,3,new QTableWidgetItem(QString::number(v[i]-v2[i],'g',4)));
            table->setItem(i,4,new QTableWidgetItem(QString::number(localErrorEst[i],'g',4)));
            table->setItem(i,5,new QTableWidgetItem(QString::number(H,'g',4)));
            table->setItem(i,6,new QTableWidgetItem(QString::number(c1[i])));
            table->setItem(i,7,new QTableWidgetItem(QString::number(c2[i])));
        }
        table->show();

    }

    delete[] c1;
    delete[] c2;

}
